package com.beiding.service.impl;

import com.beiding.config.EmailAmqpConfig;
import com.beiding.dao.OrderDao;
import com.beiding.dao.ResourceOperations;
import com.beiding.dao.SettingsDao;
import com.beiding.dao.KeyValueDao;
import com.beiding.exception.AuthenticationException;
import com.beiding.exception.BadRequestException;
import com.beiding.exception.NotFoundException;
import com.beiding.pojo.Email;
import com.beiding.pojo.order.Evaluation;
import com.beiding.pojo.order.Order;
import com.beiding.pojo.payload.Payload;
import com.beiding.pojo.room.Room;
import com.beiding.service.AuthService;
import com.beiding.service.OrderService;
import com.beiding.service.RoomStorageService;
import com.beiding.timeout.bean.TimeShaft;
import com.beiding.utils.LogUtils;
import org.bson.Document;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Random;

@Service
public class OrderServiceImpl implements OrderService {

    private OrderDao orderDao;
    private RoomStorageService roomStorageService;
    private TimeShaft<String> timeShaft;
    private MessageSender sender;

    private SettingsDao settings;

    @Autowired
    public void setSettings(SettingsDao settings) {
        this.settings = settings;
    }


    @Autowired
    public void setSender(MessageSender sender) {
        this.sender = sender;
    }

    @Autowired
    public void setTimeShaft(TimeShaft<String> timeShaft) {
        this.timeShaft = timeShaft;
    }

    @Autowired
    public void setRoomStorageService(RoomStorageService roomStorageService) {
        this.roomStorageService = roomStorageService;
    }

    @Autowired
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    private Payload update(Order order) {
        return Payload.create("order.update").order(order);
    }


    private boolean judge(Order order, int... states) {

        if (order == null) {
            return false;
        }

        for (int s : states) {
            if (s == order.getState()) {
                return true;
            }
        }

        return false;

    }


    private Order getInsensitive(String id) {
        return orderDao.getInsensitive(id);
    }


    private void state(String id, int state) {
        orderDao.updateField(id, "state", state);
    }


    //如果在付款后采取获取房源,那么再待付款订单那一栏里面会获取不到房源信息
    @Override
    public String save(Order order) {

        Room room = roomStorageService.get(order.getRoomId());

        if (room == null) {
            throw new NotFoundException("未找到指定的房源");
        }

        if (room.getTheUser() != null) {
            throw new BadRequestException("房间被占用中,无法使用");
        }

        order.setPlatformCostRate(settings.getPlatformCostRate());

        order.setRoom(room);

        //保存房源
        orderDao.save(order);

        //房间超时未进行付款
        timeShaft.delay("pay", settings.getTimeout("pay"), order.getId());

        return order.getId();

    }


    @Override
    public void remove(String id) {

        Order order = getInsensitive(id);

        if (judge(order, Order.CREATE)) {

            orderDao.deleteById(id);

            //付款超时任务失效
            timeShaft.invalidate("pay", id);

            //只是把状态修改为终止,并没有真正删除
            state(id, Order.TERMINATION);

        } else if (judge(order, Order.EVALUATE)) {

            //默认好评
            Evaluation evaluate = new Evaluation();
            evaluate.setEvaluation("默认好评");
            evaluate.setService(5);
            evaluate.setEnvironment(5);

            try {
                evaluate(id, evaluate);
            } catch (Exception ignore) {
            }

            state(id, Order.TERMINATION);

        } else {
            throw new BadRequestException("当前时期不可删除");
        }


    }

    @Override
    public void paid(String id) {

        Order order = getInsensitive(id);

        //只能在创建以后才能支付
        if (!judge(order, Order.CREATE)) {
            throw new BadRequestException("支付状态错误");
        }


        state(id, Order.PAID);

        //支付超时任务失效
        timeShaft.invalidate("pay", id);

        timeShaft.delay("accept", settings.getTimeout("accept"), id);

        order.setState(Order.PAID);

        sender.sendToLandlord(order.getLandlord(), update(order).msg("你有一笔待受理订单"));

    }


    @Override
    public void evaluate(String id, Evaluation evaluate) {

        Order order = getInsensitive(id);

        if (order != null) {

            if (order.getState() != Order.EVALUATE) {
                throw new BadRequestException("当前状态不可评价");
            }

            evaluate.setHolderId(order.getRoomId());

            evaluate.setValuer(order.getHolderId());

            //将评论提交至房源中心
            roomStorageService.evaluate(evaluate);

            //订单结束
            state(id, Order.TERMINATION);

            timeShaft.invalidate("evaluate", id);

            timeShaft.delay("delete", settings.getTimeout("delete"), id);

        }
    }

    @Override
    public List<Order> getNonPayment(String name) {
        return orderDao.getNonPayment(name);
    }

    @Override
    public List<Order> getPayed(String name) {
        return orderDao.getPayed(name);
    }

    @Override
    public List<Order> getEvaluate(String name) {
        return orderDao.getEvaluate(name);
    }


    //该方法用于提供情况下的退款操作.一种是付过款还没有受理.一种是受理后,房东拒绝退款房客同意扣20%的强制退款
    @Override
    public Order refund(String id) {

        Order order = getInsensitive(id);

        if (!judge(order, Order.PAID, Order.REFUND_DISPUTE)) {
            throw new BadRequestException("订单状态与预期不相符");
        }

        state(id, Order.OVERDUE);

        if (judge(order, Order.REFUND_DISPUTE)) {

            //释放房源
            roomStorageService.release(order.getRoomId());

            order.setState(Order.OVERDUE);

            sender.sendToLandlord(order.getLandlord(), update(order).msg("房客申请强制退款,您将受到订单的20%作为补偿"));
        }

        //将等待入住任务失效
        timeShaft.invalidate("checkIn", id);

        LogUtils.info(this.getClass(), "执行异步退款操作");

        return order;
    }

    @Override
    public Order refund(String id, String reason) {

        Order order = getInsensitive(id);

        //只有受理和订单被锁死的时候才可使用该方法进行退款操作
        if (!judge(order, Order.ACCEPTED, Order.LOCK)) {
            throw new BadRequestException("订单状态与预期不相符");
        }


        if (order.getRefundTime() > 2) {
            throw new BadRequestException("退款次数已达上限");
        }

        Document document = new Document();

        document.put("refundReason", reason);

        //增加一个退款次数
        document.put("refundTime", order.getRefundTime() + 1);

        //订单进入退款状态
        document.put("state", Order.REFUND);

        orderDao.updateFields(id, document);

        //将等待入住任务失效
        timeShaft.invalidate("checkIn", id);

        //等待房东办理退款业务
        timeShaft.delay("refund", settings.getTimeout("refund"), id);

        LogUtils.info(this.getClass(), "通知房东有待处理的退款");

        order.setRefundReason(reason);

        order.setState(Order.REFUND);

        //退款会直接显示在页面上
        sender.sendToLandlord(order.getLandlord(), update(order).msg("你有一笔待处理的退款"));

        return order;
    }

    @Override
    public void checkRoomHolder(String id, String name) {

        Order order = get(id, "landlord");

        if (order == null || !order.getLandlord().equals(name)) {
            throw new AuthenticationException("无权操作!");
        }


    }

    @Override
    public void accept(String id) {

        //进行受理操作

        Order order = getInsensitive(id);

        //只有在付款后才能进行受理操作
        if (order.getState() != Order.PAID) {
            throw new BadRequestException("状态异常");
        }

        try {
            roomStorageService.occupy(order.getRoomId(), order.getHolderId());
        } catch (Exception e) {
            throw new BadRequestException(e.getMessage());
        }


        //生成入住码
        String code = makeCheckInKey();

        Document document = new Document();

        document.put("state", Order.ACCEPTED);
        document.put("acceptedTimestamp", System.currentTimeMillis());
        document.put("checkInKey", code);

        orderDao.updateFields(id, document);


        //等待房客入住
        timeShaft.invalidate("accept", id);
        timeShaft.delay("checkIn", settings.getTimeout("checkIn"), id);

        order.setState(Order.ACCEPTED);

        sender.sendToTenant(order.getHolderId(), update(order).msg("你的订单已被受理"));


    }


    private Random random = new Random();
    private DecimalFormat df = new DecimalFormat("0000");

    //生成一个四位入住码
    private String makeCheckInKey() {
        return df.format(random.nextInt(9999));
    }


    @Override
    public String checkIn(String id, String checkInKey) {

        Order order = get(id);

        //首先查看订单是否已经被锁死
        if (order.getState() == Order.LOCK) {
            return "lock";
        }

        //检查订单状态
        if (order.getState() != Order.ACCEPTED) {
            throw new BadRequestException("状态异常");
        }

        if (checkInKey.equals(order.getCheckInKey())) {

            order.setUseTimestamp(System.currentTimeMillis());

            //订单进入使用使用状态
            order.setState(Order.USING);

            //完整更新
            orderDao.save(order);

            timeShaft.invalidate("checkIn", id);

            timeShaft.delay("expire", order.getUsingMilliseconds(), id);


            //清理敏感数据

            order.setCheckInKeyErrorTime(null);

            sender.sendToTenant(order.getHolderId(), update(order));

            return "success";
        } else {
            Integer checkInKeyErrorTime = order.getCheckInKeyErrorTime();

            checkInKeyErrorTime++;

            //连续6次输错入住码,订单失效
            if (checkInKeyErrorTime >= 6) {

                //订单失效
                state(id, Order.OVERDUE);

                timeShaft.invalidate("unlock", id);

                //启用退款流程
                LogUtils.info(this.getClass(), "订单失效,执行退款流程");

                return "termination";
            } else {

                orderDao.updateField(id, "checkInKeyErrorTime", checkInKeyErrorTime);
                if (checkInKeyErrorTime == 3) {
                    state(id, Order.LOCK);

                    timeShaft.delay("unlock", settings.getTimeout("unlock"), id);

                    sentUnlockEmail(order.getLandlord(), null, id);

                    return "lock";
                }

                return "failed";

            }

        }


    }

    @Override
    public List<Order> getOrdersByRoomHolder(String name) {
        return orderDao.getToDoOrder(name);
    }

    @Override
    public void disagreeRefund(String id) {

        Order order = getInsensitive(id);
        if (!judge(order, Order.REFUND)) {
            throw new BadRequestException("状态错误");
        }

        //订单进入纠纷状态
        state(id, Order.REFUND_DISPUTE);

        timeShaft.invalidate("refund", id);

        order.setState(Order.REFUND_DISPUTE);

        sender.sendToTenant(order.getHolderId(), update(order).msg("房东拒绝了您的推断请求"));

    }

    @Override
    public void agreeRefund(String id) {

        Order order = getInsensitive(id);

        if (!judge(order, Order.REFUND)) {
            throw new BadRequestException("状态错误");
        }

        //订单失效
        state(id, Order.OVERDUE);

        //释放房间占用
        roomStorageService.release(order.getRoomId());

        timeShaft.invalidate("refund", id);

        //异步退款
        LogUtils.info(this.getClass(), "退款");

        order.setState(Order.OVERDUE);

        sender.sendToTenant(order.getHolderId(), update(order).msg("房东同意了您的退款,我们将在两个工作日内,退还您所有费用"));

    }


    @Override
    public void active(String id, String name) {

        Order order = getInsensitive(id);

        if (order != null && order.getLandlord().equals(name)) {

            //将订单恢复到受理状态
            state(id, Order.ACCEPTED);

            timeShaft.invalidate("unlock", id);

            order.setState(Order.ACCEPTED);

            sender.sendToLandlord(order.getLandlord(), update(order));

        }

    }

    @Override
    public void withholdDeposit(String id, String withholdDepositReason, float depositWithhold) {

        Order order = getInsensitive(id);

        //只有退还押金的阶段才可以扣押金
        if (!judge(order, Order.REFUND_DEPOSIT)) {
            throw new BadRequestException("状态异常");
        }


        if (depositWithhold > order.getDeposit()) {
            throw new BadRequestException("扣留押金不能超过押金总额");
        }

        Document document = new Document();

        //订单进入扣留押金的状态
        document.put("state", Order.WITHHOLD_DEPOSIT);

        document.put("withholdDepositReason", withholdDepositReason);

        document.put("depositWithhold", depositWithhold);

        orderDao.updateFields(id, document);

        //房客超时未处理
        timeShaft.invalidate("refundDispute", id);
        timeShaft.delay("withholdDeposit", settings.getTimeout("withholdDeposit"), id);

        order.setState(Order.WITHHOLD_DEPOSIT);

        order.setWithholdDepositReason(withholdDepositReason);

        order.setDepositWithhold(depositWithhold);

        sender.sendToTenant(order.getTenant(), update(order).msg("房东要求扣留您的押金,请及时处理"));

    }


    //同意扣留押金
    @Override
    public void agreeWithholdDeposit(String id) {

        Order order = getInsensitive(id);

        if (!judge(order, Order.WITHHOLD_DEPOSIT)) {
            throw new BadRequestException("状态异常");
        }

        //订单进入评价状态
        state(id, Order.EVALUATE);

        timeShaft.invalidate("withholdDeposit", id);
        timeShaft.delay("evaluate", settings.getTimeout("evaluate"), id);

        //异步执行扣留押金
        LogUtils.info(this.getClass(), "执行扣留押金操作");

        order.setState(Order.EVALUATE);

        sender.sendToLandlord(order.getLandlord(), update(order).msg("房客同意了您扣留押金的请求"));

    }

    //不同意扣留押金
    @Override
    public void disagreeWithholdDeposit(String id) {

        Order order = getInsensitive(id);

        if (!judge(order, Order.WITHHOLD_DEPOSIT)) {
            throw new BadRequestException("状态异常");
        }

        //进入争议状态
        state(id, Order.WITHHOLD_DEPOSIT_DISPUTE);

        //等待争议超时
        timeShaft.invalidate("withholdDeposit", id);
        timeShaft.delay("withholdDepositDispute", settings.getTimeout("withholdDepositDispute"), id);

        order.setState(Order.WITHHOLD_DEPOSIT_DISPUTE);

        sender.sendToLandlord(order.getLandlord(), update(order).msg("房客不同意您扣留押金,您可以在两天内申请仲裁"));

    }


    //同意退押金
    @Override
    public void agreeRefundDeposit(String id) {

        Order order = getInsensitive(id);

        if (!judge(order, Order.REFUND_DEPOSIT)) {
            throw new BadRequestException("状态异常");
        }


        //更新状态位为评论
        state(id, Order.EVALUATE);


        //将超时退还押金失效
        timeShaft.invalidate("refundDeposit", id);

        //等待默认好评
        timeShaft.delay("evaluate", settings.getTimeout("evaluate"), id);

        //异步退押金
        LogUtils.info(this.getClass(), "退还押金");

        order.setState(Order.EVALUATE);

        //更新房客订单.修改为评价的订单
        sender.sendToTenant(order.getTenant(), update(order));

        //当退还完成以后给前端发送一条消息,告知已成功退还
        sender.sendToLandlord(order.getLandlord(), update(order));

    }

    @Override
    public int revocationRefund(String id) {


        Order order = getInsensitive(id);

        if (!judge(order, Order.REFUND)) {
            throw new BadRequestException("状态异常");
        }

        state(id, Order.ACCEPTED);

        timeShaft.invalidate("refund", id);

        timeShaft.delay("checkIn", settings.getTimeout("checkIn"), id);

        order.setState(Order.ACCEPTED);

        sender.sendToLandlord(order.getLandlord(), update(order));

        return order.getRefundTime();

    }

    @Override
    public void arbitrationWithholdDeposit(String id) {

        Order order = getInsensitive(id);

        if (!judge(order, Order.WITHHOLD_DEPOSIT_DISPUTE)) {
            throw new BadRequestException("订单状态异常");
        }

        //订单进入仲裁状态
        state(id, Order.WITHHOLD_DEPOSIT_ARBITRATION);

        timeShaft.invalidate("withholdDepositDispute", id);

        LogUtils.info(this.getClass(), "仲裁人员接受任务,对两方进行调解");

        order.setState(Order.WITHHOLD_DEPOSIT_ARBITRATION);

        sender.sendToTenant(order.getTenant(), update(order));

    }

    @Override
    public void arbitrationRefund(String id) {

        Order order = getInsensitive(id);

        if (!judge(order, Order.REFUND_DISPUTE)) {
            throw new BadRequestException("订单状态异常");
        }

        //订单进入仲裁状态
        state(id, Order.REFUND_ARBITRATION);

        //释放房间占用
        roomStorageService.release(order.getRoomId());

        //申请仲裁
        timeShaft.invalidate("refund", id);

        LogUtils.info(this.getClass(), "仲裁人员接受任务,对两方进行调解");

        order.setState(Order.REFUND_ARBITRATION);

        sender.sendToLandlord(order.getLandlord(), update(order));

    }

    private AuthService authService;

    private RabbitTemplate rabbitTemplate;

    private KeyValueDao tokenDao;

    @Autowired
    public void setTokenDao(KeyValueDao tokenDao) {
        this.tokenDao = tokenDao;
    }

    @Autowired
    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Autowired
    public void setAuthService(AuthService authService) {
        this.authService = authService;
    }

    @Override
    public void sentUnlockEmail(String name, String email, String orderId) {


        if (email != null) {

            //首先检查订单状态是否锁死

            Order order = getInsensitive(orderId);

            if (!judge(order, Order.LOCK)) {
                throw new BadRequestException("订单异常状态");
            }

            //发送邮件

        } else {
            email = authService.getEmail(name);
        }

        String token = name + "_unlockOrder_" + orderId + df.format(random.nextInt(9999));

        //保存token 一小时有效
        tokenDao.put(token, orderId, 3600);

        String path = currentContextPath() + "/landlord/order/unlock?token=" + token;

        //发送邮件
        rabbitTemplate.convertAndSend(EmailAmqpConfig.SEND_EMAIL_QUEUE, new Email(email, "点击以下链接激活订单:" + path, "订单激活"));


    }

    @Override
    public void unlockOrder(String token) {

        String orderId = (String) tokenDao.get(token);

        state(orderId, Order.ACCEPTED);

        Order order = getInsensitive(orderId);

        //给前端发消息告知已经解锁
        sender.sendToLandlord(order.getLandlord(), update(order));

        tokenDao.remove(token);

    }


    private String currentContextPath() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();


        String url = request.getRequestURL().toString();

        return url.split(request.getServletPath())[0];

    }

    @Override
    public ResourceOperations<Order> resourceOperations() {
        return orderDao;
    }
}
